home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / DCLAP 6d / dclap6d / DNet / DGoList.cpp < prev    next >
Text File  |  1996-07-05  |  29KB  |  1,120 lines

  1. // DGopherList.cp 
  2. // by d.g. gilbert, Mar 1992 
  3. // dclap version jan 94
  4. /*
  5.             This code is Copyright (C) 1992 by D.G. Gilbert.
  6.             All Rights Reserved.
  7.  
  8.             You may use this code for your personal use, to provide a non-profit
  9.             service to others, or to use as a test platform for a commercial
  10.             implementation. 
  11.  
  12.             You may not use this code in a commercial product, nor to provide a
  13.             commercial service, nor may you sell this code without express
  14.             written permission of the author.  
  15.             
  16.             gilbertd@bio.indiana.edu 
  17.             Biology Dept., Indiana University, Bloomington, IN 47405 
  18. */
  19.  
  20.  
  21. #include <ncbi.h>
  22. #include <dgg.h>
  23.  
  24. #include "DTCP.h"
  25. #include "DGopher.h"
  26. #include "DURL.h"
  27. #include "DGoList.h"
  28. #include "DGoPlus.h"
  29. #include "DGoInit.h"
  30. #include "DGoClasses.h"
  31.  
  32. #include <DList.h>
  33. //#include <DView.h>
  34.  
  35. // this dup's DGopher.cpp ! Beware -- need public const DGopher::kLocalhost
  36. Local const char* kLocalhost = "localhost";
  37.  
  38.  
  39. // DGopherList ------------------------------
  40.  
  41. #define Inherited DList
  42.  
  43. // static
  44. short dglCompareGophers( DGopher* gopher1, DGopher* gopher2)
  45. {
  46.     if ( gopher1->Equals( gopher2) ) 
  47.         return 0;
  48. #if 0
  49.     char*    name1= (char*) (gopher1)->GetName();
  50.     char*    name2= (char*) (gopher2)->GetName();
  51.     short val= StrICmp( name1, name2);
  52.     if (val) return val;
  53. #endif
  54.     else if (gopher1 > gopher2) return 1;
  55.     else return -1;
  56.  
  57.  
  58. DGopherList::DGopherList(  DGopher* parentMenu) :
  59.     DList(),             //::dglCompareGophers),  // comparitor !?
  60.     fStatusLine(NULL),
  61.     fNewGopher(NULL),
  62.     fListUnknowns(gListUnknowns),
  63.     fSortOrder(kSortByItem),
  64.     fParentMenu(parentMenu)
  65. {
  66.     gNeedTypeChange= false;
  67. }
  68.  
  69. void DGopherList::FreeAllObjects()
  70. {
  71.     long i, n= this->GetSize();
  72.     for (i= 0; i<n; i++) {
  73.         DGopher* obj= GopherAt(i);
  74.         if (obj->GetOwnerCount() <= 1) delete obj;
  75.         else obj->suicide(); // this alone doens't call obj destructors !!
  76.         }
  77.     this->DeleteAll();
  78.  
  79. Boolean DGopherList::suicide(void) 
  80.     if (GetOwnerCount() <= 1) { 
  81.         delete this; 
  82.         return true; 
  83.         }
  84.     else 
  85.         return DObject::suicide();
  86. }
  87.  
  88. Boolean DGopherList::suicide(short ownercount)
  89. {
  90.     if (ownercount < 1) {
  91.         delete this; 
  92.         return true; 
  93.         }
  94.     else 
  95.         return DObject::suicide(ownercount);
  96. }
  97.  
  98. void DGopherList::SetStatus(DView *statusline)
  99. {
  100.     fStatusLine= statusline;
  101. }
  102.  
  103.  
  104. // static
  105. DGopher* DGopherList::NewGopher(char itemType, char* info, char* plusinfo,
  106.      short& status, DGopherList* ownerList)
  107. {
  108.     DGopher*    theGopher= NULL;
  109.     short        isPlus = status;
  110.     Boolean    hasAsk = false;
  111.     
  112.     status= DGopher::kNewGopherOkay;
  113.         /* Recursion note: could this sometimes lead to inf. loop w/ wrong choice
  114.             of server && local gopher types? */
  115.  
  116.     if (itemType == '+') {
  117.         // ?? a read error on gopher+ item ...
  118.         }
  119.  
  120.     if (gDoSuffix2MacMap && gGopherMap) {
  121.         DGopherMap* mapper= gGopherMap->MatchGopherType( itemType, false);
  122.         if (mapper) itemType= mapper->fLocalType;
  123.         }
  124.  
  125.     switch (itemType) {
  126.         case kTypeNull:
  127.         case kTypeEndOfData    :  
  128.             theGopher= NULL;
  129.             status= DGopher::kNewGopherEnd;  //'.', end of information 
  130.             break;
  131.             
  132.         case kTypeFile       : theGopher= new DTextGopher; break;
  133.         case kTypeFolder    : theGopher= new DFolderGopher; break;
  134.         case kTypeQuery   : theGopher= new DIndexGopher; break;
  135.         case kTypeBinhex    : theGopher= new DBinhexGopher; break;
  136.         case kTypeTelnet  : theGopher= new DTelnetGopher; break;
  137. #if FIX_LATER
  138.         case kTypeUuencode: theGopher= new DUuencodeGopher; break;
  139.         case kTypeCSO            : theGopher= new DCSOPhoneBookGopher; break;
  140.         case kTypeWhois        : theGopher= new DWhoisPhoneBookGopher; break;
  141. #endif // FIX_LATER
  142.         case kTypeBinary  : theGopher= new DBinaryGopher; break;
  143.         case kTypeSound     : theGopher= new DSoundGopher; break;
  144.         case kTypeGif            :             
  145.         case kTypeImage        : theGopher= new DImageGopher; break;
  146.         case kTypeMovie        : theGopher= new DMovieGopher; break;
  147.         case kTypeNote        : theGopher= new DNoteGopher; break;
  148.         case kMailType        : theGopher= new DMailGopher; break;
  149.         case kTypeHtml       : theGopher= new DHtmlGopher; break;
  150.         //case kTypeTn3270     : theGopher= new DTN3270Gopher; break;
  151.         //case kTypeMime       : theGopher= new DGopher; break;
  152.         //case kTypeBinhexpc: theGopher= new DGopher; break;
  153.  
  154.         case kTypeError :      
  155.         default    : 
  156.             if (gListUnknowns) theGopher= new DGopher;  
  157.             else {
  158.                 theGopher= NULL;
  159.                 status= DGopher::kNewGopherNone;
  160.                 }
  161.             break;
  162.         }
  163.  
  164.     if (status == DGopher::kNewGopherOkay && info) { 
  165.         theGopher->SetLink( itemType, info);
  166.         if (theGopher->fIplus) {   
  167.             char *pp= theGopher->fLink + theGopher->fIplus; 
  168.             hasAsk= (*pp == '?');
  169.             if (hasAsk || *pp == '+' || *pp == '#') isPlus= kGopherPlusYes;
  170.             }
  171.         theGopher->SetOwner( ownerList);  
  172.         theGopher->SetPlusInfo( isPlus, hasAsk, plusinfo);
  173.         
  174.         if (theGopher->fIpath == 0 && theGopher->fIhost == 0) {
  175.             delete theGopher; 
  176.             theGopher= NULL;
  177.             status= DGopher::kNewGopherNone;
  178.             }
  179.         else if (gNeedTypeChange) {  
  180.             // Recursion alert: CopyToNewKind calls back NewGopher, but not to here...info==nil
  181.           DGopher* bGopher= 
  182.               DGopherList::CopyToNewKind( theGopher->fType, theGopher, ownerList);
  183.           if (bGopher!=NULL) {
  184.                 delete theGopher; 
  185.                 theGopher= bGopher;
  186.                 }
  187.             }
  188.          gNeedTypeChange= false; 
  189.         }
  190.     return theGopher;    
  191. }
  192.  
  193.  
  194. // static
  195. DGopher* DGopherList::CopyToNewKind(char itemType, DGopher* srcGopher, 
  196.                                     DGopherList* ownerList, Boolean copyMacStuff)
  197. {
  198.     short    status = srcGopher->fIsPlus;
  199.     DGopher* destGopher= DGopherList::NewGopher( itemType, NULL, NULL, status, ownerList);
  200.     if (status == DGopher::kNewGopherOkay) {
  201.             //destGopher->SetLink( itemType, *srcGopher->fLink);
  202.         destGopher->CopyGopher( srcGopher);
  203.  
  204.             // THIS IS a QUICK FIX for moving srcGopher to destGopher + delete srcGopher...
  205.             // this code is probably no longer needed, as we clone fViews,fAskers in CopyGopher
  206.         if (srcGopher->fViews && !destGopher->fViews) { 
  207.             destGopher->fViews= srcGopher->fViews; 
  208.             srcGopher->fViews= NULL;
  209.             }
  210.         if (srcGopher->fAskers && !destGopher->fAskers) { 
  211.             destGopher->fAskers= srcGopher->fAskers; 
  212.             srcGopher->fAskers= NULL;
  213.             }
  214.         if (srcGopher->fAbstract && !destGopher->fAbstract) { 
  215.             destGopher->fAbstract= srcGopher->fAbstract; 
  216.             srcGopher->fAbstract= NULL;
  217.             }
  218.         
  219.             // !! Need to change VIEW types to coincide w/ this (e.g., from text/plain to app/...)
  220.         destGopher->fType= itemType;
  221.         if (itemType != srcGopher->fType) {
  222.             destGopher->DeleteViews();
  223.             }
  224.  
  225.         if (copyMacStuff) {
  226.             destGopher->fTransferType    = srcGopher->fTransferType;  
  227.             destGopher->fMacType    = srcGopher->fMacType;              
  228.             destGopher->fMacSire    = srcGopher->fMacSire;     
  229.             destGopher->fSaveToDisk    = srcGopher->fSaveToDisk;     
  230.             destGopher->fLaunch        = srcGopher->fLaunch;     
  231.             }
  232.  
  233.         return destGopher;
  234.         }    
  235.     else
  236.         return NULL;    
  237. }
  238.  
  239.  
  240. // static
  241. short DGopherList::CompareGophers( void* gopher1, void* gopher2)
  242. {
  243.     if ( ((DGopher*) gopher1)->Equals( (DGopher*) gopher2) ) 
  244.         return 0;
  245.  
  246.     char*    name1= (char*) ((DGopher*)gopher1)->GetName();
  247.     char*    name2= (char*) ((DGopher*)gopher2)->GetName();
  248.     short val= StrICmp( name1, name2);
  249.     if (val) return val;
  250.     else if (gopher1 > gopher2) return 1;
  251.     else return -1;
  252.  
  253.  
  254.  
  255. short dglCompareByType( void* item1, void* item2)
  256. {
  257.     char    name1= ((DGopher*)item1)->fType;
  258.     char    name2= ((DGopher*)item2)->fType;
  259.     if (name1 < name2) return -1;
  260.     else if (name1 > name2) return 1;
  261.     else  return 0;
  262. }
  263.  
  264.  
  265. short dglCompareBySize( void* item1, void* item2)
  266. {
  267.     // We want INVERTED sort -- greatest size first
  268.     unsigned long    name1= ((DGopher*)item1)->fSizeLong;
  269.     unsigned long    name2= ((DGopher*)item2)->fSizeLong;
  270.     if (name1 > name2) return -1;
  271.     else if (name1 < name2) return 1;
  272.     else  return 0;
  273. }
  274.  
  275. short dglCompareByDate( void* item1, void* item2)
  276. {
  277.     // We want INVERTED sort -- greatest date first
  278.     unsigned long    name1= ((DGopher*)item1)->fDateLong;
  279.     unsigned long    name2= ((DGopher*)item2)->fDateLong;
  280.     if (name1 > name2) return -1;
  281.     else if (name1 < name2) return 1;
  282.     else  return 0;
  283. }
  284.  
  285. short dglCompareByHost(void* item1, void* item2)
  286. {
  287.     char*    name1= (char*) ((DGopher*)item1)->GetHost();
  288.     char*    name2= (char*) ((DGopher*)item2)->GetHost();
  289.     return StrICmp( name1, name2);
  290. }
  291.  
  292. short dglCompareByName( void* item1, void* item2)
  293. {
  294.     char*    name1= (char*) ((DGopher*)item1)->GetName();
  295.     char*    name2= (char*) ((DGopher*)item2)->GetName();
  296.     return StrICmp( name1, name2);
  297. }
  298.  
  299.  
  300. Boolean DGopherList::SortList(Sorts sortorder)
  301. {
  302.     if (sortorder == fSortOrder)
  303.         return false;
  304.     else {
  305.         fSortOrder= sortorder;
  306.         switch( sortorder) {
  307.             case kSortByKind :
  308.                 this->SortBy( dglCompareByType);
  309.                 break;
  310.             case kSortBySize : 
  311.                 this->SortBy( dglCompareBySize);
  312.                 break;
  313.             case kSortByDate :
  314.                 this->SortBy( dglCompareByDate);
  315.                 break;
  316.             case kSortByHost : 
  317.                 this->SortBy( dglCompareByHost);
  318.                 break;
  319.             case kSortByName : 
  320.                 this->SortBy( dglCompareByName);
  321.                 break;
  322.             case kSortByItem: 
  323.             default:
  324.                 this->Sort();
  325.                 break;
  326.             }
  327.         return true;
  328.         }
  329. }
  330.  
  331.  
  332.  
  333. Boolean DGopherList::MakeNewGopher( char *pInfo, char *eInfo, 
  334.                                                     char *pPlus, char *ePlus, short status)
  335. {
  336.     char    termInfo, termPlus;
  337.     if (eInfo) { termInfo= *eInfo; *eInfo= 0; }
  338.     if (ePlus) { termPlus = *ePlus; *ePlus= 0;  } 
  339.     fNewGopher= DGopherList::NewGopher( *pInfo, pInfo, pPlus, status, this);
  340.     if (eInfo) *eInfo= termInfo;
  341.     if (ePlus) *ePlus= termPlus;
  342.     switch (status) {
  343.         case DGopher::kNewGopherEnd : return false;
  344.         case DGopher::kNewGopherNone: return true; 
  345.         case DGopher::kNewGopherOkay: this->InsertLast( fNewGopher); return true;
  346.         }
  347.     return false;
  348. }
  349.  
  350.  
  351.  
  352. Boolean DGopherList::IsGopherLine( char *line)
  353. {
  354.     // this is a static function, usable w/o a DGopherListDoc object
  355.     // look for something like ( | == tab)
  356.     // start w/ optional "+INFO: "
  357.     // 1Any title string|optional path string|required host string|required port string|optional +
  358.     enum { kTitle, kPath, kHost, kPort, kPlus };
  359.     const short    kTabsInGopherItem = 3;
  360.  
  361.     char *np, *ep, *cp = line;
  362.     np= StrChr( cp, '\n'); 
  363.     if (!np) np= StrChr( cp, '\r');
  364.     if (!np) np= StrChr( cp, '\0'); // ?? or return false here
  365.     for (short itab=0; itab<kTabsInGopherItem; itab++) {
  366.         if ( (ep= StrChr( cp, '\t')) == NULL) 
  367.             return false;
  368.         else if (ep > np)
  369.             return false;
  370.         else switch (itab) {
  371.             case kTitle: if (ep < cp+2) return false; break;
  372.             case kPath: break; // can be null
  373.             case kHost: break; //<< null here in error type... //if (ep < cp+2) return false;
  374.             case kPort: if (ep < cp+1) return false; break;
  375.             }
  376.         cp = ep+1;
  377.         }
  378.     return true;
  379. }
  380.  
  381.  
  382. // static
  383. DGopher* DGopherList::GopherFromURL( char* url, long urlsize, 
  384.                                                     DGopherList* itsList, Boolean doInsertLast, char itemType)
  385. {
  386.     short    status;
  387.     char    calltype;
  388.     char *info = "0Link from URL\t0localdoc\tlocalhost\t70";
  389.     char *plusinfo = NULL;
  390.     DGopher* theGo;
  391.     
  392.     calltype= itemType;
  393.     if (!itemType) itemType= '0';
  394.     theGo= DGopherList::NewGopher( itemType, info, plusinfo, status, itsList);
  395.     switch (status) {
  396.  
  397.         case DGopher::kNewGopherOkay: {
  398.                 char oldtype= theGo->fType; 
  399.                 if (DURL::ParseURL( theGo, url, urlsize, true)) {
  400.                   if (calltype) 
  401.                         theGo->fType= calltype;
  402.                     else if (theGo->fType != oldtype) {
  403.                         DGopher* bGopher= 
  404.                             DGopherList::CopyToNewKind( theGo->fType, theGo, itsList);
  405.                       if (bGopher) {
  406.                             delete theGo; 
  407.                             theGo= bGopher;
  408.                             }
  409.                         }
  410.                     if (doInsertLast && itsList) itsList->InsertLast( theGo); 
  411.                     }
  412.                 else {
  413.                     delete theGo; 
  414.                     theGo= NULL;
  415.                     }    
  416.                 }
  417.                 break;
  418.  
  419.         case DGopher::kNewGopherEnd :  
  420.         case DGopher::kNewGopherNone: 
  421.                 if (theGo) delete theGo; 
  422.                 theGo= NULL;
  423.                 break;
  424.         }
  425.     if (itsList) itsList->fNewGopher= theGo;
  426.     return theGo;
  427. }
  428.  
  429. // static
  430.  
  431. DGopher*  DGopherList::LocalGopher( char* name)
  432. {    
  433.     char docurl[1024];
  434.     DGopher* theGo= NULL;
  435.     char   quote= '\'';    
  436.     if (StrChr( name, quote)) quote= '"';
  437.     StrCpy(docurl, " file:///");
  438.     StrNCat(docurl, name, 1000);
  439.     StrNCat(docurl, " ", 1000); 
  440.     docurl[1023]= 0;
  441.     long doclen= StrLen(docurl);
  442.     docurl[0]= quote; // so parseURL doesn't eat spaces
  443.     docurl[doclen-1]= quote;
  444.     theGo= DGopherList::GopherFromURL( docurl, doclen, NULL, false, 0);
  445.     if (theGo) theGo->StoreName( (char*)gFileManager->FilenameFromPath(name));
  446.     return theGo;
  447. }
  448.  
  449. DGopher*  DGopherList::LocalGopher( DFile* theFile)
  450. {    
  451.     return LocalGopher(  (char*) theFile->GetName());
  452. }
  453.  
  454.  
  455.  
  456.  
  457.  
  458. void DGopherList::ReadLinks( char* linkptr)
  459. {    
  460.     if (!linkptr) return;
  461.     ReadLinks( linkptr, StrLen(linkptr));
  462. }
  463.  
  464. void DGopherList::ReadLinks( char *linkptr, long len)
  465. {
  466.     Boolean done= false, haveinfo= false;
  467.     register char     *cp, *ci;
  468.     char        *cpend, *ep= NULL, *pInfo= NULL, *eInfo= NULL, *pPlus= NULL;
  469.     short     pluskind = kGopherPlusDontKnow;
  470.     
  471.             // ?? parse different possible formats:
  472.             //    \n+INFO: 
  473.             //  \n?...name...\t...path...\t...host...\t...#port...+maybe more\n
  474.             //  \nName=...\nHost=...\nPath=...\nPort=...\nType=...\nOther=
  475.             //    URL:? &/or   gopher://   or  ftp://  or other://
  476.             //
  477.             // ? some gopher+ server fails to send CR-LF, just sends LF ?!?
  478.             // also check for 3 tabs in first/info line -- skip till found to avoid bad data
  479.             // from some stupid servers.
  480.             
  481.     if (!linkptr) return;
  482.     cp= pInfo= linkptr;
  483.     cpend= cp + len; //-1;  
  484.     Boolean isPlus= (*cp == '+');
  485.  
  486.     while (!done && cp) {
  487.         ep= cp;
  488.         while (*cp < ' ' && *cp != 0 && cp != cpend) cp++;    // leave leading spaces in line !?!!
  489.         if (*cp == 0 || cp == cpend) done= true; // break;  !! need to process data yet
  490.         if (*cp == '+' && (*ep == kCR || *ep == kLF) && 0 == Strncasecmp( cp, "+INFO:", 6)) 
  491.               isPlus= true; 
  492.  
  493.         if (isPlus) {
  494.             Boolean atinfo = (0 == Strncasecmp( cp, "+INFO:", 6) );
  495.             
  496.             if (haveinfo && (atinfo || done)) {
  497.                 char* ePlus= (ep > pPlus) ? ep : pPlus;
  498.                 pluskind = kGopherPlusDontKnow; //kGopherPlusYes
  499.                 if (! MakeNewGopher( pInfo, eInfo, pPlus, ePlus, pluskind))  
  500.                     done= true; 
  501.                 haveinfo= false;
  502.                 pInfo= eInfo= pPlus= NULL;
  503.                 }
  504.             
  505.             if (done) 
  506.                 break; // nada
  507.                 
  508.             else if (atinfo) {
  509.                 haveinfo= true;
  510.                 cp += 6;
  511.                 while (*cp <= ' ' && *cp != 0 && cp != cpend) cp++;
  512.                 pInfo= cp;
  513.                 //eInfo= strchr(pInfo, kCR); if (!eInfo) eInfo= strchr(pInfo, kLF);
  514.                 for (ci= pInfo, eInfo= NULL; ci<cpend && *ci!=0; ci++) 
  515.                     if (*ci == kCR || *ci == kLF) { eInfo= ci; ci= cpend;}
  516.                         
  517.                 if (eInfo) {
  518.                     pPlus= eInfo+1; 
  519.                     while (*pPlus <= ' ' && *pPlus != 0 && pPlus != cpend) pPlus++;
  520.                     }
  521.                 else pPlus= NULL;
  522.                 }
  523.      
  524.             else if (*cp != '+' && *cp != ' ') { 
  525.                     // this may be Directory- list from gopher+ server
  526.                 char    *tp = NULL, *lastt;
  527.                 short  itab = 0;
  528.                 for (ci= cp; ci<cpend && *ci!=0; ci++) if (*ci == '\t') { tp= ci; ci= cpend; }
  529.                 if (tp) {
  530.                     lastt = tp+1;
  531.                     for (itab=0; itab<4 && tp!=NULL; itab++) { 
  532.                         lastt= tp+1; 
  533.                         tp= NULL;
  534.                         for (ci= lastt; ci<cpend && *ci!=0; ci++) if (*ci == '\t') { tp= ci; ci= cpend; }
  535.                         }
  536.                     }
  537.                 if (itab>2) {
  538. #if 1
  539.                     pluskind =kGopherPlusDontKnow;
  540. #else
  541.                     if (itab > 3 && *lastt == '+') pluskind = kGopherPlusYes;
  542.                     else pluskind = kGopherPlusNo;
  543. #endif
  544.                     pInfo= cp;
  545.                     for (ci= pInfo, eInfo= NULL; ci<cpend && *ci!=0; ci++) 
  546.                         if (*ci == kCR || *ci == kLF) { eInfo= ci; ci= cpend;}
  547.                     if (! MakeNewGopher( pInfo, eInfo, NULL, NULL, pluskind)) done= true;        
  548.                     }
  549.                 }
  550.  
  551.             }
  552.             
  553.         else if (done) 
  554.                 break; 
  555.                 
  556.         else if (IsGopherLine( cp)) {
  557.             pInfo= cp;
  558.             pluskind =kGopherPlusDontKnow;
  559.             for (ci= pInfo, eInfo= NULL; ci<cpend && *ci!=0; ci++) 
  560.                 if (*ci == kCR || *ci == kLF) { eInfo= ci; ci= cpend;}
  561.             if (! MakeNewGopher( pInfo, eInfo, NULL, NULL, pluskind))
  562.                 done= true;        
  563.             }
  564.  
  565.         else if (0 == Strncasecmp( cp, "+URL:", 5) ) {
  566.             cp += 5;
  567.             for (ci= cp, eInfo= NULL; ci<cpend && *ci!=0; ci++) 
  568.                 if (*ci == kCR || *ci == kLF) { eInfo= ci; ci= cpend;}
  569.             char* saven= StrDup( (char*)fNewGopher->GetName());
  570.             if (eInfo && DURL::ParseURL( fNewGopher, cp, eInfo-cp)) ;
  571.             if (saven && *saven) fNewGopher->StoreName(saven); 
  572.             MemFree(saven);
  573.             }
  574.  
  575.         //cp= strchr(cp, kCR); if (!cp) cp= strchr(cp, kLF);
  576.         for (ci= cp, cp= NULL; ci<cpend && *ci!=0; ci++) 
  577.             if (*ci == kCR || *ci == kLF) { cp= ci; ci= cpend;}
  578.         }
  579. }
  580.  
  581.  
  582.  
  583. char* DGopherList::WriteLinks(short indent)
  584. {
  585.     char*     h= (char*) MemGet(1,true);
  586.     long i, n= this->GetSize();
  587.     for (i= 0; i<n; i++) {
  588.         DGopher* ag= (DGopher*) this->At(i);
  589.         ag->ToText(h, indent);
  590.         }
  591.     return h;
  592. }
  593.  
  594.  
  595.  
  596. char* DGopherList::WriteLinksForServer()
  597. //Note: cannot currently READ this format -- .link form on unix servers 
  598. {
  599.     short        itemnum= 0;
  600.     char*     h= (char*) MemGet(1,true);
  601.     long i, n= this->GetSize();
  602.     for (i= 0; i<n; i++) {
  603.         DGopher* ag= (DGopher*) this->At(i);
  604.         ag->ToServerText(h, i+1);
  605.         }
  606.     return h;
  607. }
  608.  
  609.  
  610. char* DGopherList::WriteLinksForDisplay( 
  611.                 Boolean showDate, Boolean showSize, Boolean showKind, Boolean showPath, 
  612.                 Boolean showHost, Boolean showPort, Boolean showAdmin) // pretty print
  613. {
  614.     short        itemnum= 0;
  615.     char*     h= (char*) MemGet(1,true);
  616.     long i, n= this->GetSize();
  617.     for (i= 0; i<n; i++) {
  618.         DGopher* ag= (DGopher*) this->At(i);
  619.         ag->ToPrettyText( h, i+1, 
  620.                 showDate, showSize, showKind,
  621.                 showPath, showHost, showPort, showAdmin);
  622.         }
  623.     return h;
  624. }
  625.  
  626.  
  627.  
  628.  
  629.  
  630.  
  631.  
  632.  
  633.  
  634. #ifdef OS_MAC
  635.  
  636. #ifndef __TYPES__
  637. #include <Types.h>
  638. #endif
  639. #ifndef __MEMORY__
  640. #include <Memory.h>
  641. #endif
  642. #ifndef __OSUTILS__
  643. #include <OSUtils.h>
  644. #endif
  645. #ifndef __FILES__
  646. #include <Files.h>
  647. #endif
  648. #ifndef __FOLDERS__
  649. #include <Folders.h>
  650. #endif
  651.  
  652. #endif
  653.  
  654. #if defined(OS_DOS) || defined (OS_NT)
  655. #include <dir.h>
  656. #include <dos.h>
  657. #include <io.h>
  658. #endif
  659.  
  660. #ifdef OS_UNIX
  661. #include <dirent.h>
  662. #include <unistd.h>
  663. #endif
  664.  
  665.  
  666. class DLoGoInfo : public DObject
  667. {
  668. public:
  669.     char    fKind;
  670.     char * fName, * fDate, * fView;
  671.     const char * fTitle;
  672.     long    fSize;
  673.     Boolean fPlus, fIsAsk, fHasAbs;
  674.     DLoGoInfo(char kind, const char* name, const char* title, 
  675.                         const char* view, const char* date, long size) {
  676.         fKind= kind;
  677.         fSize= size;
  678.         fView= (char*) view; // note no StrDup
  679.         fName= StrDup(name);
  680.         if (title) fTitle= title; else fTitle= fName;
  681.         fDate= StrDup(date);
  682.         fPlus= true;
  683.         fIsAsk= fHasAbs= false;
  684.         }
  685.     ~DLoGoInfo() {
  686.         MemFree(fName);
  687.         MemFree(fDate);
  688.         }
  689. };
  690.  
  691.  
  692. short dglLoGoCompareByName( void* item1, void* item2)
  693. {
  694.     char*    name1= (char*) ((DLoGoInfo*)item1)->fTitle; //fName;
  695.     char*    name2= (char*) ((DLoGoInfo*)item2)->fTitle; //fName;
  696.     return StrICmp( name1, name2);
  697. }
  698.  
  699.  
  700. Local void StoreLoGoWais( short startwais, short endwais, DList* dirlist)
  701. {
  702.     short     j;
  703.     char    * suf;
  704.     DLoGoInfo * jinfo;
  705.     
  706.     jinfo= (DLoGoInfo*) dirlist->At(startwais);
  707.     jinfo->fKind= kTypeQuery;
  708.     jinfo->fView= "application/gopher-menu"; //???
  709.     //jinfo->fPlus= false; // ?? so app doesn't query for view info
  710.     suf= StrRChr( jinfo->fName, '.');
  711.     if (suf) *suf= 0;
  712.     for (j= startwais+1; j<endwais; j++) {
  713.         jinfo= (DLoGoInfo*) dirlist->At(j);
  714.         jinfo->fKind= 0;
  715.         }
  716.     //nwais= 0;
  717. }
  718.  
  719.  
  720. // static
  721. short DGopherList::LocalFolder2GopherList( DFile* outFile, char* folderpath)
  722. {
  723.     DList* dirlist= NULL;
  724.     DLoGoInfo* info;
  725.     char    namestore[256], pathstore[256], *nameat, buf[512];
  726.     long    pathlen, date, ksize;
  727.     char    * path, * view, * title, gokind;
  728.     char* kUpfolderTitle =  " <-- Parent folder";
  729.     
  730.     if (!outFile || !folderpath || !*folderpath) 
  731.         return -1;
  732.     short port= DGopher::StandardPort( DGopher::kGopherprot);
  733.     pathlen= StrLen(folderpath);
  734.     StrNCpy(namestore, folderpath, 240);
  735.     StrNCpy(pathstore, folderpath, 240);
  736.  
  737. #ifdef OS_MAC
  738. {
  739.     OSErr                 error = noErr;
  740.     long                     dirID = 0, theDirID;
  741.     short                 vRefNum = 0, itemIndex = 1; /* start with 1, then use what's returned */
  742.     CInfoPBRec         pb;
  743.     Boolean             isDirectory;
  744.     StringPtr         name;
  745.     DateTimeRec      dt;
  746.  
  747.     if (pathstore[pathlen-1] != ':') {
  748.         pathstore[pathlen++]= ':'; 
  749.         pathstore[pathlen]= 0;
  750.         }
  751.     nameat= pathstore+pathlen;
  752.     
  753.     Nlm_CtoPstr(namestore);
  754.     name= (StringPtr) namestore;
  755.     error = DetermineVRefNum( name, vRefNum, &pb.hFileInfo.ioVRefNum);
  756.     if ( error != noErr ) return error;
  757.     
  758.     error = GetDirID( vRefNum, dirID, name, &theDirID, &isDirectory);
  759.     if ( error != noErr ) return error;
  760.     else if ( !isDirectory ) return error;
  761.  
  762.     dirlist= new DList();
  763.     while ( error == noErr ) {  
  764.         pb.hFileInfo.ioNamePtr = name;
  765.         pb.hFileInfo.ioDirID = theDirID;
  766.         pb.hFileInfo.ioFDirIndex = itemIndex;
  767.         error = PBGetCatInfoSync(&pb);
  768.         if ( error == noErr ) {
  769.             if (itemIndex==1) {
  770.                 info= new DLoGoInfo( kTypeFolder, ":", kUpfolderTitle,
  771.                                         "application/gopher+-menu", "19530424134647", 0);
  772.                 dirlist->InsertLast( info);
  773.                 }
  774.             ++itemIndex;    /* prepare to get next item in directory */
  775.             
  776.             //title= pb.hFileInfo.ioNamePtr; == name
  777.             Nlm_PtoCstr( namestore);
  778.             //StrNCpy( nameat, namestore, 256-pathlen);
  779.             
  780.             if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) { 
  781.                 gokind= kTypeFolder;
  782.                 view= "application/gopher+-menu";
  783.                 date= pb.dirInfo.ioDrMdDat;
  784.                 ksize= pb.dirInfo.ioDrNmFls;
  785.                 }
  786.                 
  787.             else {    
  788.                 //?? need to handle some special file types --
  789.                 // !! esp. need to map waissrc files to query type ??
  790.                 // -- map Mac Types: TEXT, ...? to gopher 0
  791.                 // map GIFf, PICT, BINA, ????, to various go types...
  792.                 // ?? use mapper classes !!?
  793.                 // !! need to check for .ASK blocks & ASK exec/scripts & handle that !?
  794.                 // !? need some form of .LINK file for changing go type/name !?
  795.                 
  796.                 gokind= kTypeFile;
  797.                 view= "text/plain";
  798.                 date= pb.hFileInfo.ioFlMdDat;
  799.                 ksize= pb.hFileInfo.ioFlLgLen / 1024;
  800.                 }
  801.                     
  802.             // Mod-Date: Thu Feb  4 13:46:47 1993 <19930204134647>
  803.             // write <19930204134647> == 1993 02 04 13 46 47
  804.             Secs2Date( date, &dt); 
  805.             sprintf( buf, "%04d%02d%02d%02d%02d%02d",  
  806.                             dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
  807.             info= new DLoGoInfo( gokind, namestore, NULL, view, buf, ksize);
  808.             dirlist->InsertLast( info);
  809.             }
  810.         }
  811. }
  812. #endif
  813.  
  814. #ifdef OS_UNIX
  815. {
  816.     // use readdir() routines to list directory
  817.     // and stat(filename,info) to get size/date/ of file
  818.     struct stat    statbuf;
  819.   DIR        * theDir;
  820.     struct dirent  *afile;
  821.     char         timeval[16];
  822.     struct tm *tmthing;
  823.     char    curdir[512];
  824.     char    *name1;
  825.   
  826.     if (pathstore[pathlen-1] != '/') {
  827.         pathstore[pathlen++]= '/'; 
  828.         pathstore[pathlen]= 0;
  829.         }
  830.     nameat= pathstore+pathlen;
  831.  
  832. #define USECHDIR 1
  833. #ifdef USECHDIR
  834.   getcwd( curdir, sizeof(curdir));
  835.     if (chdir(namestore)<0) return -1; // ??
  836.     theDir = opendir(".");
  837. #else
  838.     theDir = opendir(namestore);
  839. #endif
  840.     if (!theDir) return -1;
  841.     
  842.     dirlist= new DList();
  843.     for (afile = readdir(theDir); afile != NULL; afile = readdir(theDir)) {
  844.  
  845.       StrNCpy( namestore, afile->d_name, sizeof(namestore));
  846.         name1= namestore;
  847. #ifndef USECHDIR
  848.         StrNCpy( nameat, namestore, sizeof(pathstore)-pathlen);
  849.         name1= nameat;
  850. #endif
  851.  
  852.       if (! stat( name1, &statbuf)) {
  853.             if (StrCmp(namestore, ".")==0) {
  854.                 gokind= 0;
  855.                 view= NULL;
  856.                 }
  857.             else if (StrCmp(namestore, "..")==0) {
  858.                 gokind= kTypeFolder;
  859.                 view= "application/gopher+-menu";
  860.                 title= kUpfolderTitle;
  861.                 }
  862.             else if ( S_ISDIR(statbuf.st_mode)) {
  863.                 gokind= kTypeFolder;
  864.                 view= "application/gopher+-menu";
  865.                 }
  866.             else {
  867.                 gokind= kTypeFile;
  868.                 view= "text/plain";
  869.                 }
  870.             ksize= statbuf.st_size / 1024;
  871.             
  872.                 // Mod-Date: Thu Feb  4 13:46:47 1993 <19930204134647>
  873.             tmthing = localtime(&(statbuf.st_mtime));
  874.             strftime( timeval, sizeof(timeval), "%Y%m%d%H%M%S", tmthing);
  875.             sprintf( buf, "%s", timeval);
  876.             info= new DLoGoInfo( gokind, namestore, NULL, view, buf, ksize);
  877.             dirlist->InsertLast( info);
  878.             }
  879.         }
  880. #ifdef USECHDIR
  881.     chdir(curdir);
  882. #endif
  883. }
  884. #endif
  885.  
  886. #if defined(OS_DOS) || defined (OS_NT)
  887. {
  888.     struct  ffblk  finfo;
  889.     long        ltime;
  890.     struct    ftime    dt, *dtp;
  891.     
  892.     if ((pathstore[pathlen-1] != '\\') && (pathstore[pathlen-1] != '*')) {
  893.         pathstore[pathlen++]= '\\';   // "/" alone will get folder
  894.         pathstore[pathlen++]= '*';   // ?? "/*.*" will get folder' contents
  895.         pathstore[pathlen++]= '.';
  896.         pathstore[pathlen++]= '*';
  897.         pathstore[pathlen]= 0;
  898.     pathlen -= 3; // drop *.* for nameat !!
  899.         }
  900.     nameat= pathstore+pathlen;
  901.     
  902.     short err= findfirst( pathstore, &finfo, FA_RDONLY | FA_DIREC);
  903.     if (err) return -1;
  904.     dirlist= new DList();
  905.     while ( err==0 ) {
  906.     
  907.       StrNCpy( namestore, finfo.ff_name, sizeof(namestore));
  908.         //StrNCpy( nameat, namestore, sizeof(pathstore)-pathlen);
  909.       title= NULL;
  910.         if (StrCmp(namestore, ".")==0) {
  911.             gokind= 0;
  912.             view= NULL;
  913.             }
  914.         else if (StrCmp(namestore, "..")==0) {
  915.             gokind= kTypeFolder;
  916.             view= "application/gopher+-menu";
  917.             title= kUpfolderTitle;
  918.             }
  919.         else if ( finfo.ff_attrib & FA_DIREC) {
  920.             gokind= kTypeFolder;
  921.             view= "application/gopher+-menu";
  922.             }
  923.         else {
  924.             gokind= kTypeFile;
  925.             view= "text/plain";
  926.             }
  927.         ksize= finfo.ff_fsize / 1024;
  928.                 
  929.         // Mod-Date: Thu Feb  4 13:46:47 1993 <19930204134647>
  930.         // dos file times are weird
  931.         ltime= (finfo.ff_fdate << 16 + finfo.ff_ftime); // this is a GUESS !! probly wrong
  932.         //dt = (ftime) ltime;
  933.     MemCpy( &dt, <ime, sizeof(dt));
  934.         sprintf(buf, "%04d%02d%02d%02d%02d%02d",
  935.              (dt.ft_year + 1980), dt.ft_month, dt.ft_day, 
  936.                             dt.ft_hour, dt.ft_min, dt.ft_tsec*2);
  937.         info= new DLoGoInfo( gokind, namestore, title, view, buf, ksize);
  938.         dirlist->InsertLast( info);
  939.         
  940.         err= findnext( &finfo);
  941.         }
  942. }
  943. #endif
  944.  
  945.         // other OSes, don't know how to write directory
  946.         
  947.         
  948.     if (dirlist) {
  949.         // do various checks here:
  950.         // -- handle .ASK blocks
  951.         // -- handle WAIS index.{dct,dlm,doc,fmt,fni,fnn,hl,inv,src,query.lock}
  952.         //      and index_field_*.dct, index_field_*.inv
  953.         // -- handle any .LINK files
  954.  
  955.         short        startwais, stopwais, nwais= 0;
  956.         Boolean    gotwais;
  957.         char        indexname[256];
  958.         char        * name, * suf, * plus;
  959.         long        namelen, indexlen;
  960.         DLoGoInfo * info, * jinfo;
  961.         short i, n= dirlist->GetSize();
  962.  
  963.         dirlist->SortBy( dglLoGoCompareByName);
  964.         
  965.         for (i=0; i <= n; i++) {
  966.             gotwais= false;
  967.             if (i<n) {
  968.                 info= (DLoGoInfo*) dirlist->At(i);
  969.                 name= info->fName;
  970.                 suf= StrRChr( name, '.');
  971.                 namelen= StrLen(name);
  972.                 }
  973.                 
  974.             if (suf) {
  975.                 namelen= MIN( sizeof(indexname), namelen-StrLen(suf));
  976.                 suf++;
  977.                 
  978.                 if (StrICmp( suf, "ask") == 0 && i>0) {
  979.                     // handle .ask
  980.                     jinfo= (DLoGoInfo*) dirlist->At(i-1);
  981.                     if (StrNCmp(jinfo->fName, info->fName, namelen)==0) {
  982.                         jinfo->fIsAsk= true; // jinfo is the application for the .ask block !?
  983.                         info->fKind= 0; // don't show
  984.                         }
  985.                     }
  986.                     
  987. #if defined(OS_DOS) || defined(OS_NT)
  988.                  if (StrICmp( suf, "abs") == 0 && i>0)
  989. #else
  990.                 if (StrICmp( suf, "abstract") == 0 && i>0)
  991. #endif
  992.                     {
  993.                     jinfo= (DLoGoInfo*) dirlist->At(i-1);
  994.                     if (StrNCmp(jinfo->fName, info->fName, namelen)==0) {
  995.                         jinfo->fHasAbs= true; // jinfo is the application for the .abs block !?
  996.                          info->fKind= 0; // don't show
  997.                         }
  998.                      }
  999.                     
  1000.                 else if ( nwais && StrStr(name,indexname) == name) {
  1001.                     if ( ( namelen == indexlen || StrStr(name,"_field_") != NULL 
  1002.                         || StrICmp(suf,"lock") == 0) 
  1003.                     && (StrICmp( suf, "dlm")==0
  1004.                      || StrICmp( suf, "dct")==0    // this is for field, but watch for new nonfld !
  1005.                      || StrICmp( suf, "doc")==0
  1006.                      || StrICmp( suf, "fmt")==0
  1007.                      || StrICmp( suf, "fni")==0
  1008.                      || StrICmp( suf, "fnn")==0
  1009.                      || StrICmp( suf, "hl" )==0
  1010.                      || StrICmp( suf, "inv")==0
  1011.                      || StrICmp( suf, "lock")==0
  1012.                      || StrICmp( suf, "src")==0 )
  1013.                      ) {
  1014.                         nwais++;
  1015.                         gotwais= true;
  1016.                         }
  1017.                     }
  1018.                 else if ( StrICmp( suf, "dct")==0) { 
  1019.                     // always 1st in sorted list of wais names
  1020.                     // do this "dct" test after above, as "_field_" dct would conflict
  1021.                     if (nwais) {
  1022.                         StoreLoGoWais( startwais, i, dirlist);
  1023.                         nwais= 0;
  1024.                         }
  1025.                     gotwais= true;
  1026.                     nwais= 1;
  1027.                     startwais= i;
  1028.                     StrNCpy(indexname, name, namelen);
  1029.                     indexname[namelen]= 0;
  1030.                     indexlen= namelen;
  1031.                     }
  1032.                 }
  1033.                 
  1034.             if (nwais && !gotwais) {
  1035.                 StoreLoGoWais( startwais, i, dirlist);
  1036.                 nwais= 0;
  1037.                 }
  1038.             }
  1039.                     
  1040.         outFile->Open("w");
  1041.         for (i=0; i<n; i++) {
  1042.             info= (DLoGoInfo*) dirlist->At(i);
  1043.             if (info->fKind) {        
  1044.                 const char * title;    
  1045.                 StrNCpy( nameat, info->fName, sizeof(pathstore)-pathlen);
  1046.                 if (info->fIsAsk) plus= "\t?";
  1047.                 else if (info->fPlus) plus= "\t+"; 
  1048.                 else plus= "";
  1049.                 
  1050.                 sprintf(buf, "+INFO: %c%s\t%s\t%s\t%d%s\n", 
  1051.                                     info->fKind, info->fTitle, pathstore, kLocalhost, port, plus);
  1052.                 outFile->WriteLine( buf);
  1053.                 
  1054.                 sprintf(buf, "+ADMIN:\n Mod-Date: %s <%s>\n", "", info->fDate);
  1055.                 outFile->WriteLine( buf);
  1056.                 
  1057.                 sprintf(buf, "+VIEWS:\n %s En_US: <%dk>\n", info->fView, info->fSize);
  1058.                 outFile->WriteLine( buf);
  1059.                 
  1060.                 if (info->fIsAsk) {
  1061.                     StrNCpy( namestore, pathstore, sizeof(namestore));
  1062.                     StrNCat( namestore, ".ask", sizeof(namestore));
  1063.                     DFile* askf= new DFile( namestore);
  1064.                     if (askf->Exists()) {
  1065.                         char* buf1;
  1066.                         askf->Open("r");
  1067.                         outFile->WriteLine( "+ASK:\n");
  1068.                         buf[0]= ' ';
  1069.                         buf1= buf+1;
  1070.                         while (!askf->Eof()) {
  1071.                             *buf1= 0;
  1072.                             if (0 == askf->ReadLine( buf1, sizeof(buf)-1))
  1073.                              outFile->WriteLine( buf);
  1074.                             }
  1075.                         outFile->WriteLine( " \n");
  1076.                         askf->Close();
  1077.                         }
  1078.                     delete askf;
  1079.                     }
  1080.                     
  1081.  
  1082.                 if (info->fHasAbs) {
  1083.                     StrNCpy( namestore, pathstore, sizeof(namestore));
  1084.                     StrNCat( namestore, ".abstract", sizeof(namestore));
  1085.                     DFile* askf= new DFile( namestore);
  1086.                     if (askf->Exists()) {
  1087.                         char* buf1;
  1088.                         askf->Open("r");
  1089.                         outFile->WriteLine( "+ABSTRACT:\n");
  1090.                         buf[0]= ' ';
  1091.                         buf1= buf+1;
  1092.                         while (!askf->Eof()) {
  1093.                             *buf1= 0;
  1094.                             if (0 == askf->ReadLine( buf1, sizeof(buf)-1))
  1095.                                 outFile->WriteLine( buf);
  1096.                             }
  1097.                         outFile->WriteLine( " \n");
  1098.                         askf->Close();
  1099.                         }
  1100.                     delete askf;
  1101.                     }
  1102.                     
  1103.                 }
  1104.             delete info;
  1105.             }
  1106.         //outFile->Close();
  1107.         delete dirlist;
  1108.         return 0;
  1109.         }
  1110.     else
  1111.         return -1;
  1112. }
  1113.  
  1114.  
  1115.  
  1116.